package mybatis.plugins;

import mybatis.builder.xml.XmlConfigBuilder;
import mybatis.builder.xml.XmlMapperBuilder;
import mybatis.exception.MyBatisException;
import mybatis.session.Configuration;
import mybatis.session.SqlSessionFactory;
import mybatis.session.defaults.DefaultSqlSessionFactory;
import org.dom4j.DocumentException;
import spring.dao.factory.FactoryBean;
import spring.dao.factory.InitializingBean;
import utils.LogUtils;
import utils.ResourceUtils;

import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Objects;

public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean {

    private final Configuration configuration;

    private DataSource dataSource;

    private String configLocation;

    private String mapperLocation;

    private SqlSessionFactory sqlSessionFactory;

    private boolean isInit = false;

    public SqlSessionFactoryBean() {
        configuration = new Configuration();
    }

    public SqlSessionFactoryBean(DataSource dataSource, String configLocation, String mapperLocation) {
        this.dataSource = dataSource;
        this.configLocation = configLocation;
        this.mapperLocation = mapperLocation;
        configuration = new Configuration();
    }

    public void init() throws IOException {
        initConfigLocation();
        initMapperLocation();
    }

    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
        configuration.setDataSource(dataSource);
    }

    public void setConfigLocation(String configLocation) {
        this.configLocation = configLocation;
    }

    public void setMapperLocation(String mapperLocation) {
        this.mapperLocation = mapperLocation;
    }

    public SqlSessionFactory build() {
        assert Objects.nonNull(dataSource);
        assert Objects.nonNull(configLocation);
        assert Objects.nonNull(mapperLocation);
        if (!this.isInit) {
            this.isInit = true;
            try {
                init();
            } catch (IOException e) {
                throw new MyBatisException(e);
            }
        }
        return new DefaultSqlSessionFactory(configuration);
    }

    private void initConfigLocation() {
        InputStream inputStream;
        try {
            inputStream = ResourceUtils.parseConfig(this, this.configLocation);
        } catch (IOException e) {
            e.printStackTrace();
            LogUtils.error("文件路径无效：%s", this.configLocation);
            throw new MyBatisException(e);
        }
        XmlConfigBuilder xmlConfigBuilder = new XmlConfigBuilder(this.configuration);
        try {
            xmlConfigBuilder.parse(inputStream);
        } catch (DocumentException e) {
            e.printStackTrace();
            throw new MyBatisException(e);
        }
    }

    private void initMapperLocation() throws IOException {
        List<InputStream> inputStreams = ResourceUtils.parseConfigs(this, this.mapperLocation);
        inputStreams.forEach(inputStream -> {
            XmlMapperBuilder xmlMapperBuilder = new XmlMapperBuilder(this.configuration);
            xmlMapperBuilder.parse(inputStream);
        });
    }

    @Override
    public void afterPropertiesSet() {
        this.sqlSessionFactory = build();
    }

    @Override
    public SqlSessionFactory getObject() {
        if (Objects.isNull(this.sqlSessionFactory)) {
            this.isInit = false;
            afterPropertiesSet();
        }
        return this.sqlSessionFactory;
    }

    @Override
    public Class<?> getObjectType() {
        return this.sqlSessionFactory == null ? SqlSessionFactory.class : this.sqlSessionFactory.getClass();
    }

    @Override
    public boolean isSingleton() {
        return true;
    }
}
